Repository navigation
fix(oauth): revoke replaced sessions and preserve token metadata - #1147
Conversation
e8d23c6 to
8125427
Compare
c01e269 to
183b2bc
Compare
|
The changelog for this PR also notes that OAuth will be the default auth method in the future. And add a link to the Coder docs. |
| signal: options?.signal, | ||
| }); | ||
| // `login` reads the token from the environment, so drop the empty flag. | ||
| const flags = cli.flags.filter((flag) => flag !== EMPTY_TOKEN_FLAG); |
There was a problem hiding this comment.
I see that we are setting --token= to prevent the CLI from using the environment variable, but this feels like using a side effect as a janky workaround when we could directly unset/set the environment variable instead as necessary.
There was a problem hiding this comment.
Agreed. Spawned CLI processes now get process.env without CODER_SESSION_TOKEN (any case, plus serpent's HOMEBREW_ fallback). --token= stays only in the SSH ProxyCommand and terminal.sendText, whose env we can't set. No other CODER_* var affects which session is used.
fdfe25b to
a20f0af
Compare
5c10f0f to
2c347e5
Compare
Lisa-Fiander
left a comment
There was a problem hiding this comment.
one small check https://github.com/coder/security-assessments/issues/367
Clean up replaced OAuth sessions without forcing a re-login when the required scopes change. - Insufficient scopes prevent refresh but keep the stored access token, so normal 401 recovery applies after expiry. Rename the check to `canRefreshOAuthSession`. - Save a successful replacement first, then revoke the overwritten OAuth pair in the background with the client registration captured before login. Failed or canceled logins, same-token reuse, and stored-session adoption revoke nothing. - Keep refresh credentials and scopes when the same token is reused on the same origin, instead of turning it into a manual-token session. If a refresh rotates the session while its stored token is checked, keep the rotated session. - Share revocation with logout through `withOAuthMetadata`, which token refresh also uses, and request `user:update_personal` to refresh expired external-auth links. - If the server refuses dynamic client registration (off by default from Coder 2.38), name the setting and offer session-token sign-in. - Start every CLI invocation's global flags with an empty `--token=`, so `CODER_SESSION_TOKEN` in the extension host (in any case) no longer overrides the stored session. This covers the SSH ProxyCommand and terminals too. `coder login --use-token-as-session` omits it, since it reads the token from the environment, and gets an environment without the host's case variants of the variable. Closes #1140
…ient - Log Axios errors as their request line and the server's reply. Their config and socket hold the session token, OAuth tokens, and client secret, which the buffered connection logs replayed into the output channel. - Record the client that issued each OAuth session, and refresh and revoke as that client, so another window re-registering the client no longer breaks either. Sessions without a recorded client use the stored registration. - Explain why the extension requests `user:update_personal`. - In the changelog, note that OAuth becomes the default sign-in method and link the OAuth2 provider docs. - Shorten comments.
- Spawned CLI processes, including the app-command terminal (with `strictEnv`), get `process.env` without `CODER_SESSION_TOKEN`, matched as the CLI reads it: any letter case, with or without the `HOMEBREW_` prefix. Only the SSH ProxyCommand keeps an inline `--token=`, since Remote-SSH sets its environment. - Save a client registration only after the OAuth login succeeds, so a failed login keeps the registration a stored session was issued to. - Rename the recorded OAuth client to `registration` to tell the registered app apart from the HTTP client. - Offer OAuth only when discovery advertises `registration_endpoint`, which Coder omits while dynamic client registration is off (the default from 2.37), instead of failing after the user picks OAuth. A notice explains the session-token fallback, and both unavailable cases are logged.
The logger summarized only a top-level Axios error. Every logged argument now goes through one redaction walk, for both the output channel and the flight-recorder buffer. It redacts credential fields with string values in plain objects and errors, and summarizes Axios errors at any depth, including error causes. The walk reads only data properties, never throws, and skips entries the channel drops.
f987fd6 to
1059ab7
Compare
Summary
Follow-up to #1138's token-cleanup comment: clean up replaced OAuth sessions without forcing an immediate re-login when required scopes change. Closes #1140: this PR covers items 1 and 3, and #1128 covers item 2.
canRefreshOAuthSession.registration(client ID and secret) its tokens were issued to, so refresh and revocation keep working when another window re-registers. Sessions saved before this change fall back to the stored registration, which login now saves only after it succeeds, so a failed login can't replace it.token,refresh_token,client_secret, ...) with string values in plain objects and errors, and reduces an Axios error at any depth, including an error'scause, to its request line, status, and the server's reply. The error's config and socket hold the session token, OAuth tokens, and client secret. The walk reads only data properties, never throws, and skips entries the channel drops.revokeOAuthTokensinsrc/oauth/revocation.tsserves logout and replacement.withOAuthMetadatainsrc/oauth/metadataClient.tscreates the client and fetches metadata for revocation and token refresh.user:update_personal, whichcoder startneeds to refresh expired external-auth links. No narrower scope exists, so it also allows profile and Git SSH key changes; a comment next to the scope says so. Inbox permissions stay optional.registration_endpoint. From Coder 2.37, discovery omits it whiledynamic_client_registration_enabledis off (the default), and the register endpoint returns 403; 2.25 to 2.36 always advertise it and always allow registration. Otherwise sign-in falls back to a session token: when DCR is off, a notice says so and a warning is logged; when the server has no OAuth discovery (404 or network error), only an info line is logged. The changelog notes that OAuth becomes the default sign-in method and links the OAuth2 provider docs.start,update,speedtest,netcheck,support bundle,ping,login,logout, and the app-command terminal withstrictEnv) getprocess.envwithoutCODER_SESSION_TOKEN, matched the way the CLI reads it: any letter case, with or without theHOMEBREW_prefix.AGENTS.mdadds this to the CLI execution rules. The SSHProxyCommandruns in an environment Remote-SSH sets, so it starts with an empty--token=instead; the CLI applies flags over the environment, and a user's own--tokenincoder.globalFlagscomes later and still wins. No otherCODER_*variable changes which session is used:--urland--use-keyringalready override theirs, andCODER_AGENT_TOKENis read only by agent commands.Out of scope: startup or remote admission gates, per-action permission maps, and a background-refresh redesign. Before expiry, users can still see feature-specific permission errors; those don't prompt for sign-in.
Change size
Diff against the merge base; counts include moved code and renames.
src/**)test/**)AGENTS.mdValidation
Four commits on top of
main.pnpm test: 191 files, 3,055 passed, 6 skipped.pnpm typecheck,pnpm lint, andpnpm format:check: passed.registration_endpointdisabling OAuth with a notice, and failed discovery falling back without one; the host session token (any case,HOMEBREW_prefix) dropped from every spawned CLI's environment, including the app-command terminal, whilestoreTokenpasses its own; the empty--token=in theProxyCommandflags with a user--tokenoverriding it; refresh and revocation as the recorded registration, with the stored registration as fallback; an Axios error, alone or nested in an error'scause, whose logged form omits its token, client secret, and session header; credential fields redacted in any logged object, on both the output channel and the buffer; and redaction that keeps references when nothing changes, never runs getters, never throws, and keeps a redacted error's stack.Known limitations
Generated by Coder Agents on behalf of @EhabY.